package com.smasher.modbus.tcp;

import android.util.Log;

import com.licheedev.modbus4android.ModbusCallback;
import com.licheedev.modbus4android.ModbusParam;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.msg.ReadCoilsResponse;
import com.serotonin.modbus4j.msg.ReadDiscreteInputsResponse;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersResponse;
import com.serotonin.modbus4j.msg.ReadInputRegistersResponse;
import com.serotonin.modbus4j.msg.WriteCoilResponse;
import com.serotonin.modbus4j.msg.WriteCoilsResponse;
import com.serotonin.modbus4j.msg.WriteRegisterResponse;
import com.serotonin.modbus4j.msg.WriteRegistersResponse;
import com.smasher.modbus.proxy.ModbusComm;
import com.smasher.modbus.tcp.rxjava.ModbusObserver;

import org.apache.commons.lang3.ArrayUtils;

import java.util.Arrays;

import io.reactivex.android.schedulers.AndroidSchedulers;

public class ModbusTCP implements ModbusComm<TcpParam> {

    private ModbusParam param;
    public static final String TAG = "ModbusAndroid";

    boolean injectLog = true;


    @Override
    public void init(TcpParam param) {
        this.param = param;
    }

    @Override
    public void open() {

        ModbusManager.get().closeModbusMaster();

        ModbusManager.get().init(param, new ModbusCallback<ModbusMaster>() {
            @Override
            public void onSuccess(ModbusMaster modbusMaster) {
                Log.d(TAG, "打开成功");
            }

            @Override
            public void onFailure(Throwable tr) {
                Log.d(TAG, "打开失败," + tr);
            }

            @Override
            public void onFinally() {

            }
        });

    }

    @Override
    public void close() {
        ModbusManager.get().closeModbusMaster();
    }

    @Override
    public void readCoils(int slave, int startAddress, int numberOfPoints) {
        send01(slave, startAddress, numberOfPoints);
    }

    @Override
    public void readCoil(int slave, int address) {
        send01(slave, address, 1);
    }

    @Override
    public void readDiscreteInputs(int slave, int startAddress, int numberOfPoints) {
        send02(slave, startAddress, numberOfPoints);
    }

    @Override
    public void readDiscreteInput(int slave, int address) {
        send02(slave, address, 1);
    }

    @Override
    public void readHoldingRegisters(int slave, int startAddress, int numberOfPoints) {
        send03(slave, startAddress, numberOfPoints);
    }

    @Override
    public void readHoldingRegister(int slave, int address) {
        send03(slave, address, 1);
    }

    @Override
    public void readInputRegisters(int slave, int startAddress, int numberOfPoints) {
        send04(slave, startAddress, numberOfPoints);
    }

    @Override
    public void readInputRegister(int slave, int address) {
        send04(slave, address, 1);
    }

    @Override
    public void writeSingleCoil(int slave, int address, boolean value) {
        send05(slave, address, value);
    }

    @Override
    public void writeSingleRegister(int slave, int address, int value) {
        send06(slave, address, value);
    }

    @Override
    public void writeCoils(int slave, int address, boolean[] data) {
        send15(slave, address, data);
    }

    @Override
    public void writeHoldingRegisters(int slave, int address, int sCount, short[] data) {
        send16(slave, address, data);
    }


    public void injectLog(String message) {
        if (injectLog) {
            Log.d(TAG, message);
        }
    }


    //================================================================================================

    public void send01(final int slaveId, final int start, final int len) {

        if (checkAmount(len)) {

            final int amount = len;

//            ModbusManager.get().readCoil(slaveId, start, len, new ModbusCallback<ReadCoilsResponse>() {
//                @Override
//                public void onSuccess(ReadCoilsResponse readCoilsResponse) {
//
//                    boolean[] sub = ArrayUtils.subarray(readCoilsResponse.getBooleanData(), 0, amount);
////                            appendText("F01读取：\n" + ArrayUtils.toString(sub) + "\n");
//                    injectLog(Arrays.toString(sub));
//                }
//
//                @Override
//                public void onFailure(Throwable tr) {
////                            appendError("F01", tr);
//                    injectLog(tr.getMessage());
//                }
//
//                @Override
//                public void onFinally() {
//
//                }
//            });

            ModbusManager.get()
                    .rxReadCoil(slaveId, start, len)
                    .subscribe(new ModbusObserver<ReadCoilsResponse>() {

                        @Override
                        public void onSuccess(ReadCoilsResponse readCoilsResponse) {
                            boolean[] sub = ArrayUtils.subarray(readCoilsResponse.getBooleanData(), 0, amount);
                            injectLog(Arrays.toString(sub));
                        }

                        @Override
                        public void onFailure(Throwable tr) {
                            injectLog(tr.getMessage());
                        }
                    });
        }
    }

    public void send02(final int slaveId, final int start, final int len) {
        if (checkAmount(len)) {

            final int amount = len;

//            ModbusManager.get().readDiscreteInput(slaveId, start, len, new ModbusCallback<ReadDiscreteInputsResponse>() {
//                @Override
//                public void onSuccess(ReadDiscreteInputsResponse readDiscreteInputsResponse) {
//
//                    boolean[] sub = ArrayUtils.subarray(readDiscreteInputsResponse.getBooleanData(), 0, amount);
////                                    appendText("F02读取：\n" + ArrayUtils.toString(sub) + "\n");
//                    injectLog(Arrays.toString(sub));
//                }
//
//                @Override
//                public void onFailure(Throwable tr) {
////                                    appendError("F02", tr);
//                    injectLog(tr.getMessage());
//                }
//
//                @Override
//                public void onFinally() {
//
//                }
//            });

            ModbusManager.get()
                    .rxReadDiscreteInput(slaveId, start, len)
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new ModbusObserver<ReadDiscreteInputsResponse>() {

                        @Override
                        public void onSuccess(ReadDiscreteInputsResponse readDiscreteInputsResponse) {
                            boolean[] sub = ArrayUtils.subarray(readDiscreteInputsResponse.getBooleanData(), 0, amount);
                            injectLog(Arrays.toString(sub));
                        }

                        @Override
                        public void onFailure(Throwable tr) {
                            injectLog(tr.getMessage());
                        }
                    });
        }
    }

    public void send03(final int slaveId, final int start, final int len) {

        if (checkAmount(len)) {

            // 普通写法
//            ModbusManager.get()
//                    .readHoldingRegisters(slaveId, start, len,
//                            new ModbusCallback<ReadHoldingRegistersResponse>() {
//                                @Override
//                                public void onSuccess(
//                                        ReadHoldingRegistersResponse readHoldingRegistersResponse) {
//                                    byte[] data = readHoldingRegistersResponse.getData();
////                            appendText("F03读取：" + ByteUtil.bytes2HexStr(data) + "\n");
//                                    onDataReceiveListener.onDataReceive(data, data.length);
//                                    injectLog(Arrays.toString(data));
//                                }
//
//                                @Override
//                                public void onFailure(Throwable tr) {
////                            appendError("F03", tr);
//                                    injectLog(tr.getMessage());
//                                }
//
//                                @Override
//                                public void onFinally() {
//
//                                }
//                            });

            // Rx写法
            ModbusManager.get()
                    .rxReadHoldingRegisters(slaveId, start, len)
                    .observeOn(AndroidSchedulers.mainThread())
//                    .compose(this.<ReadHoldingRegistersResponse>bindUntilEvent(ActivityEvent.DESTROY))
                    .subscribe(new ModbusObserver<ReadHoldingRegistersResponse>() {
                        @Override
                        public void onSuccess(ReadHoldingRegistersResponse readHoldingRegistersResponse) {
                            byte[] data = readHoldingRegistersResponse.getData();
//                            appendText("F03读取：\n" + ByteUtil.bytes2HexStr(data) + "\n");
                            injectLog(Arrays.toString(data));

                            short[] shortData = readHoldingRegistersResponse.getShortData();
//                            appendText("F03读取：\n" + ByteUtil.bytes2HexStr(data) + "\n");
                            injectLog(Arrays.toString(shortData));
                        }

                        @Override
                        public void onFailure(Throwable tr) {
//                            appendError("F03", tr);
                            injectLog(tr.getMessage());
                        }
                    });
        }
    }

    public void send04(final int slaveId, final int start, final int len) {

        if (checkAmount(len)) {

//            ModbusManager.get()
//                    .readInputRegisters(slaveId, start, len,
//                            new ModbusCallback<ReadInputRegistersResponse>() {
//                                @Override
//                                public void onSuccess(
//                                        ReadInputRegistersResponse readInputRegistersResponse) {
//                                    byte[] data = readInputRegistersResponse.getData();
////                                    appendText("F04读取：\n" + ByteUtil.bytes2HexStr(data) + "\n");
//                                    injectLog(Arrays.toString(data));
//                                }
//
//                                @Override
//                                public void onFailure(Throwable tr) {
////                                    appendError("F04", tr);
//                                    injectLog(tr.getMessage());
//                                }
//
//                                @Override
//                                public void onFinally() {
//
//                                }
//                            });

            ModbusManager.get()
                    .rxReadInputRegisters(slaveId, start, len)
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new ModbusObserver<ReadInputRegistersResponse>() {

                        @Override
                        public void onSuccess(ReadInputRegistersResponse readInputRegistersResponse) {

                            short[] shortData = readInputRegistersResponse.getShortData();
                            injectLog(Arrays.toString(shortData));

                            byte[] data = readInputRegistersResponse.getData();
//                                    appendText("F04读取：\n" + ByteUtil.bytes2HexStr(data) + "\n");
                            injectLog(Arrays.toString(data));

                        }

                        @Override
                        public void onFailure(Throwable tr) {
                            injectLog(tr.getMessage());
                        }
                    });
        }
    }

    public void send05(final int slaveId, final int start, final boolean value) {

//        ModbusManager.get()
//                .writeCoil(slaveId, start, value,
//                        new ModbusCallback<WriteCoilResponse>() {
//                            @Override
//                            public void onSuccess(WriteCoilResponse writeCoilResponse) {
////                                appendText("F05写入成功\n");
//                            }
//
//                            @Override
//                            public void onFailure(Throwable tr) {
////                                appendError("F05", tr);
//                            }
//
//                            @Override
//                            public void onFinally() {
//
//                            }
//                        });

        ModbusManager.get()
                .rxWriteCoil(slaveId, start, value)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new ModbusObserver<WriteCoilResponse>() {

                    @Override
                    public void onSuccess(WriteCoilResponse writeCoilResponse) {
                        injectLog("F05写入成功\n");
                    }

                    @Override
                    public void onFailure(Throwable tr) {
                        injectLog("F05写入失败\n");
                    }
                });
    }

    public void send06(final int slaveId, final int start, final int value) {

//        ModbusManager.get()
//                .writeSingleRegister(slaveId, start, value,
//                        new ModbusCallback<WriteRegisterResponse>() {
//                            @Override
//                            public void onSuccess(WriteRegisterResponse writeRegisterResponse) {
////                                    appendText("F06写入成功\n");
//                            }
//
//                            @Override
//                            public void onFailure(Throwable tr) {
////                                    appendError("F06", tr);
//                            }
//
//                            @Override
//                            public void onFinally() {
//
//                            }
//                        });

        ModbusManager.get()
                .rxWriteSingleRegister(slaveId, start, value)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new ModbusObserver<WriteRegisterResponse>() {

                    @Override
                    public void onSuccess(WriteRegisterResponse writeRegisterResponse) {
                        injectLog("F06写入成功\n");
                    }

                    @Override
                    public void onFailure(Throwable tr) {
                        injectLog("F06写入失败\n");
                    }
                });
    }

    public void send15(final int slaveId, final int start, final boolean[] values) {

//        ModbusManager.get()
//                .writeCoils(slaveId, start, values,
//                        new ModbusCallback<WriteCoilsResponse>() {
//                            @Override
//                            public void onSuccess(WriteCoilsResponse writeCoilsResponse) {
////                                    appendText("F15写入成功\n");
//                            }
//
//                            @Override
//                            public void onFailure(Throwable tr) {
////                                    appendError("F15", tr);
//                            }
//
//                            @Override
//                            public void onFinally() {
//
//                            }
//                        });

        ModbusManager.get()
                .rxWriteCoils(slaveId, start, values)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new ModbusObserver<WriteCoilsResponse>() {

                    @Override
                    public void onSuccess(WriteCoilsResponse writeCoilsResponse) {
                        injectLog("F15写入成功\n");
                    }

                    @Override
                    public void onFailure(Throwable tr) {
                        injectLog("F15写入失败\n");
                    }
                });
    }

    public void send16(final int slaveId, final int start, final short[] values) {


//        ModbusManager.get()
//                .writeRegisters(slaveId, start, values, new ModbusCallback<WriteRegistersResponse>() {
//            @Override
//            public void onSuccess(WriteRegistersResponse writeRegistersResponse) {
//                // 发送成功
////                                    appendText("F16写入成功\n");
//            }
//
//            @Override
//            public void onFailure(Throwable tr) {
////                                    appendError("F07", tr);
//            }
//
//            @Override
//            public void onFinally() {
//
//            }
//        });

        ModbusManager.get()
                .rxWriteRegisters(slaveId, start, values)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new ModbusObserver<WriteRegistersResponse>() {

                    @Override
                    public void onSuccess(WriteRegistersResponse writeRegistersResponse) {
                        injectLog("F16写入成功\n");
                    }

                    @Override
                    public void onFailure(Throwable tr) {
                        injectLog("F16写入失败\n");
                    }
                });
    }


//====================================================================


    /**
     * 检查数量
     */
    private boolean checkAmount(int len) {

        // 寄存器/线圈数量
        int mAmount = Integer.MIN_VALUE;
        try {
            if (len >= 1 && len <= 255) {
                mAmount = len;
            }
        } catch (NumberFormatException e) {
            //e.printStackTrace();
        }

        if (mAmount == Integer.MIN_VALUE) {
//            showOneToast("无效数量");
            return false;
        }
        return true;
    }

//    /**
//     * 检查单（寄存器）数值
//     *
//     * @return
//     */
//    private boolean checkRegValue() {
//
//        // 进制
//        int radix = mCbHex.isChecked() ? 16 : 10;
//
//        mRegValue = Integer.MIN_VALUE;
//        try {
//            int value = Integer.parseInt(mEtSingleValue.getText().toString().trim(), radix);
//            if (value >= 0 && value <= 0xFFFF) {
//                mRegValue = value;
//            }
//        } catch (NumberFormatException e) {
//            //e.printStackTrace();
//        }
//
//        if (mRegValue == Integer.MIN_VALUE) {
//            showOneToast("无效输出值");
//            return false;
//        }
//        return true;
//    }
//
//    /**
//     * 检查多个线圈数值
//     *
//     * @return
//     */
//    private boolean checkCoilValues() {
//
//        mCoilValues = null;
//        try {
//            String[] split = StringUtils.split(mEtMultiValue.getText().toString().trim(), ',');
//            ArrayList<Integer> result = new ArrayList<>();
//            for (String s : split) {
//                result.add(Integer.parseInt(s.trim()));
//            }
//            boolean[] values = new boolean[result.size()];
//            for (int i = 0; i < values.length; i++) {
//                int v = result.get(i);
//                if (v == 0 || v == 1) {
//                    values[i] = v == 1;
//                } else {
//                    throw new RuntimeException();
//                }
//            }
//
//            if (values.length > 0) {
//                mCoilValues = values;
//            }
//        } catch (Exception e) {
//            //e.printStackTrace();
//        }
//
//        if (mCoilValues == null) {
//            showOneToast("无效输出值");
//            return false;
//        }
//        return true;
//    }
//
//    /**
//     * 检查多个线圈输出值
//     *
//     * @return
//     */
//    private boolean checkRegValues() {
//
//        // 进制
//        int radix = mCbHex.isChecked() ? 16 : 10;
//
//        mRegValues = null;
//        try {
//            String[] split = StringUtils.split(mEtMultiValue.getText().toString().trim(), ',');
//            ArrayList<Integer> result = new ArrayList<>();
//            for (String s : split) {
//                result.add(Integer.parseInt(s.trim(), radix));
//            }
//            short[] values = new short[result.size()];
//            for (int i = 0; i < values.length; i++) {
//                int v = result.get(i);
//                if (v >= 0 && v <= 0xffff) {
//                    values[i] = (short) v;
//                } else {
//                    throw new RuntimeException();
//                }
//            }
//            if (values.length > 0) {
//                mRegValues = values;
//            }
//        } catch (Exception e) {
//            //e.printStackTrace();
//        }
//
//        if (mRegValues == null) {
//            showOneToast("无效输出值");
//            return false;
//        }
//        return true;
//    }


}
